package com.lingchou.cloud.process.client.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.lc.common.bean.response.Result;
import com.lingchou.cloud.process.api.dto.constant.FlowConstant;
import com.lingchou.cloud.process.api.enums.CommentTypeEnum;
import com.lingchou.cloud.process.client.dao.DefinitionSupplementaryDao;
import com.lingchou.cloud.process.client.dao.HisFlowAbleActivityDao;
import com.lingchou.cloud.process.client.dao.RunFlowAbleActivityDao;
import com.lingchou.cloud.process.client.entity.DefinitionSupplementary;
import com.lingchou.cloud.process.client.enums.GatewayFactoryEnum;
import com.lingchou.cloud.process.client.service.FlowAbleCommentService;
import com.lingchou.cloud.process.client.service.ProcessModelService;
import org.apache.commons.lang.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class ProcessModelServiceImpl implements ProcessModelService {

    private final TaskService taskService;
    private final RuntimeService runtimeService;
    private final ManagementService managementService;
    private final RunFlowAbleActivityDao runFlowAbleActivityDao;
    private final HisFlowAbleActivityDao hisFlowAbleActivityDao;
    private final FlowAbleCommentService flowAbleCommentService;
    private final DefinitionSupplementaryDao definitionSupplementaryDao;

    public ProcessModelServiceImpl(TaskService taskService, RuntimeService runtimeService, ManagementService managementService, RunFlowAbleActivityDao runFlowAbleActivityDao, HisFlowAbleActivityDao hisFlowAbleActivityDao, FlowAbleCommentService flowAbleCommentService, DefinitionSupplementaryDao definitionSupplementaryDao) {
        this.taskService = taskService;
        this.runtimeService = runtimeService;
        this.managementService = managementService;
        this.runFlowAbleActivityDao = runFlowAbleActivityDao;
        this.hisFlowAbleActivityDao = hisFlowAbleActivityDao;
        this.flowAbleCommentService = flowAbleCommentService;
        this.definitionSupplementaryDao = definitionSupplementaryDao;
    }

    /**
     * 创建会签
     *
     * @param process 流程对象
     * @param id      taskId
     * @param name    taskName
     * @return 一个可并签的userTask
     */
    @Override
    public UserTask createUserTaskCountersign(Process process, String id, String name, String collectionString, String completionCondition, String endTime, String timeServiceTaskId) {
        UserTask userTask = this.createUserTask(process, id, name);
        userTask.setAssignee("${assignee}");
        userTask.setOwner("${assignee}");
        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
        multiInstanceLoopCharacteristics.setSequential(false);
        multiInstanceLoopCharacteristics.setInputDataItem("${" + collectionString + "}");
        multiInstanceLoopCharacteristics.setElementVariable("assignee");
        multiInstanceLoopCharacteristics.setCompletionCondition(completionCondition);
        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
        //添加定时边界事件
        BoundaryEvent boundaryEvent = addBoundaryEvent(userTask, endTime);
        process.addFlowElement(boundaryEvent);
        createSequenceFlow(process, boundaryEvent.getId(), timeServiceTaskId, "ID" + IdUtil.simpleUUID(), "Name" + IdUtil.simpleUUID(), StrUtil.EMPTY);
        return userTask;
    }

    @Override
    public BoundaryEvent addBoundaryEvent(Activity attachedToRef, String endTime) {
        BoundaryEvent boundaryEvent = new BoundaryEvent();
        boundaryEvent.setId("ID" + IdUtil.simpleUUID());
        boundaryEvent.setAttachedToRef(attachedToRef);
        TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
        timerEventDefinition.setTimeDate(endTime);
        //任务结束
        boundaryEvent.setCancelActivity(true);
        boundaryEvent.setEventDefinitions(Collections.singletonList(timerEventDefinition));
        return boundaryEvent;
    }

    @Override
    public UserTask createUserTaskWithSign(Process process, String id, String name, String collectionString, String endTime, String timeServiceTaskId) {
        return createUserTaskCountersign(process, id, name, collectionString, "${nrOfCompletedInstances/nrOfInstances == 1}", endTime, timeServiceTaskId);
    }

    @Override
    public UserTask createUserTaskOrSign(Process process, String id, String name, String collectionString, String endTime, String timeServiceTaskId) {
        return createUserTaskCountersign(process, id, name, collectionString, "${nrOfCompletedInstances == 1}", endTime, timeServiceTaskId);
    }

    /**
     * 创建userTask 并锁定人员
     *
     * @param process  流程对象
     * @param id       指定 id
     * @param name     指定 name
     * @param assignee 指定人员
     * @return userTask对象
     */
    @Override
    public UserTask createAssigneeUserTask(Process process, String id, String name, String assignee, String endTime, String timeServiceTaskId) {
        UserTask userTask = this.createUserTask(process, id, name);
        userTask.setAssignee(assignee);
        userTask.setOwner(assignee);
        //添加定时边界事件
        BoundaryEvent boundaryEvent = addBoundaryEvent(userTask, endTime);
        process.addFlowElement(boundaryEvent);
        userTask.setDueDate(endTime);
        createSequenceFlow(process, boundaryEvent.getId(), timeServiceTaskId, "ID" + IdUtil.simpleUUID(), "Name" + IdUtil.simpleUUID(), StrUtil.EMPTY);
        return userTask;
    }

    /**
     * 创建userTask
     *
     * @param process 流程对象
     * @param id      指定 id
     * @param name    指定 name
     * @return userTask 对象
     */
    @Override
    public UserTask createUserTask(Process process, String id, String name) {
        UserTask userTask = new UserTask();
        userTask.setName(name);
        userTask.setId(id);
        process.addFlowElement(userTask);
        return userTask;
    }

    /**
     * 创建连线
     *
     * @param process             流程对象
     * @param from                起始元素 id
     * @param to                  指向元素 id
     * @param id                  指定 id
     * @param name                指定 name
     * @param conditionExpression 条件表达式
     */
    @Override
    public SequenceFlow createSequenceFlow(Process process, String from, String to, String id, String name, String conditionExpression) {
        SequenceFlow flow = new SequenceFlow();
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        flow.setName(name);
        flow.setId(id);
        if (StrUtil.isNotEmpty(conditionExpression)) {
            flow.setConditionExpression(conditionExpression);
        }
        process.addFlowElement(flow);
        return flow;
    }

    /**
     * 生成网关
     *
     * @param gatewayFactoryEnum 网关工厂枚举
     * @param process            流程对象
     * @param id                 指定 id
     * @param name               指定 name
     * @return 网关对象
     */
    @Override
    public Gateway createGateway(GatewayFactoryEnum gatewayFactoryEnum, Process process, String id, String name) {
        Gateway gateway = gatewayFactoryEnum.getGateWay();
        gateway.setId(id);
        gateway.setName(name);
        process.addFlowElement(gateway);
        return gateway;
    }

    /**
     * 创建开始节点
     *
     * @param process 流程对象
     * @param id      指定元素 id
     * @param name    指定元素 name
     */
    @Override
    public StartEvent createStartEvent(Process process, String id, String name) {
        StartEvent startEvent = new StartEvent();
        startEvent.setId(id);
        startEvent.setName(name);
        process.addFlowElement(startEvent);
        return startEvent;
    }

    /**
     * 创建结束节点
     *
     * @param process 流程对象
     * @param id      指定元素id
     * @param name    指定元素name
     * @return 结束事件对象
     */
    @Override
    public EndEvent createEndEvent(Process process, String id, String name) {
        EndEvent endEvent = new EndEvent();
        endEvent.setId(id);
        endEvent.setName(name);
        process.addFlowElement(endEvent);
        return endEvent;
    }

    @Override
    public void addListener(UserTask userTask,String event,String implementation) {
        FlowableListener flowableListener = new FlowableListener();
        flowableListener.setEvent(event);
        flowableListener.setImplementationType("delegateExpression");
        flowableListener.setImplementation(implementation);
        userTask.setTaskListeners(Collections.singletonList(flowableListener));
    }

    @Override
    public ServiceTask createServiceTask(Process process, String id, String name) {
        ServiceTask serviceTask = new ServiceTask();
        serviceTask.setId(id);
        serviceTask.setName(name);
        serviceTask.setImplementationType("delegateExpression");
        serviceTask.setImplementation("${timeServiceTask}");
        process.addFlowElement(serviceTask);
        return serviceTask;
    }

    @Override
    public TaskEntity createSubTask(TaskEntity parentTask, String assignee) {
        return this.createSubTask(parentTask, parentTask.getId(), assignee);
    }

    @Override
    public TaskEntity createSubTask(TaskEntity parentTask, String parentTaskId, String assignee) {
        if (parentTask == null) {
            return null;
        }
        TaskEntity task = (TaskEntity) taskService.newTask(IdUtil.simpleUUID());
        task.setCategory(parentTask.getCategory());
        task.setDescription(parentTask.getDescription());
        task.setTenantId(parentTask.getTenantId());
        task.setAssignee(assignee);
        task.setOwner(assignee);
        task.setName(parentTask.getName());
        task.setParentTaskId(parentTaskId);
        task.setProcessDefinitionId(parentTask.getProcessDefinitionId());
        task.setProcessInstanceId(parentTask.getProcessInstanceId());
        task.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
        task.setTaskDefinitionId(parentTask.getTaskDefinitionId());
        task.setPriority(parentTask.getPriority());
        task.setCreateTime(new Date());
        taskService.saveTask(task);
        return task;
    }

    @Override
    public void deleteActivity(String disActivityId, String processInstanceId) {
        String tableName = managementService.getTableName(ActivityInstanceEntity.class);
        String sql = "select t.* from " + tableName + " t where t.PROC_INST_ID_=#{processInstanceId} and t.ACT_ID_ = #{disActivityId} " +
                " order by t.END_TIME_ ASC";
        List<ActivityInstance> disActivities = runtimeService.createNativeActivityInstanceQuery().sql(sql)
                .parameter("processInstanceId", processInstanceId)
                .parameter("disActivityId", disActivityId).list();
        //删除运行时和历史节点信息
        if (!disActivities.isEmpty()) {
            ActivityInstance activityInstance = disActivities.get(0);
            sql = "select t.* from " + tableName + " t where t.PROC_INST_ID_=#{processInstanceId} and (t.END_TIME_ >= #{endTime} or t.END_TIME_ is null)";
            List<ActivityInstance> datas = runtimeService.createNativeActivityInstanceQuery().sql(sql).parameter("processInstanceId", processInstanceId)
                    .parameter("endTime", activityInstance.getEndTime()).list();
            List<String> runActivityIds = new ArrayList<>();
            if (!datas.isEmpty()) {
                datas.forEach(ai -> runActivityIds.add(ai.getId()));
                runFlowAbleActivityDao.deleteRunActivityByIds(runActivityIds);
                hisFlowAbleActivityDao.deleteHisActivityByIds(runActivityIds);
            }
        }
    }

    @Override
    public void moveExecutionsToSingleActivityId(List<String> executionIds, String activityId) {
        runtimeService.createChangeActivityStateBuilder()
                .moveExecutionsToSingleActivityId(executionIds, activityId)
                .changeState();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<String> addSignTask(String taskId, List<String> userIds, String operator, String comment, boolean flag) {
        TaskEntityImpl taskEntity = (TaskEntityImpl) taskService.createTaskQuery().taskId(taskId).singleResult();
        if (taskEntity == null) {
            return Result.failure("任务不存在");
        }
        //如果是加签再加签
        String parentTaskId = taskEntity.getParentTaskId();
        if (StringUtils.isBlank(parentTaskId)) {
            taskEntity.setOwner(operator);
            taskEntity.setAssignee(null);
            taskEntity.setCountEnabled(true);
            if (flag) {
                taskEntity.setScopeType(FlowConstant.AFTER_ADDSIGN);
            } else {
                taskEntity.setScopeType(FlowConstant.BEFORE_ADDSIGN);
            }
            //1.2 设置任务为空执行者
            taskService.saveTask(taskEntity);
        }
        //2.添加加签数据
        this.createSignSubTasks(userIds, taskEntity, operator);
        //3.添加审批意见
        String type = flag ? CommentTypeEnum.ADD_AFTER_SIGN.toString() : CommentTypeEnum.ADD_BEFORE_SIGN.toString();
        flowAbleCommentService.addComment(taskId, operator, taskEntity.getProcessInstanceId(),
                type, comment);
        String message = flag ? "后加签成功" : "前加签成功";
        return Result.success(null, message);
    }

    /**
     * 创建加签子任务
     *
     * @param userIds    被加签人
     * @param taskEntity 父任务
     * @param operator   操作人
     */
    private void createSignSubTasks(List<String> userIds, TaskEntity taskEntity, String operator) {
        String parentTaskId = taskEntity.getParentTaskId();
        if (StringUtils.isBlank(parentTaskId)) {
            parentTaskId = taskEntity.getId();
        }
        String finalParentTaskId = parentTaskId;
        //1.创建被加签人任务列表
        userIds.forEach(userId -> this.createSubTask(taskEntity, finalParentTaskId, userId));
        String taskId = taskEntity.getId();
        if (StringUtils.isBlank(taskEntity.getParentTaskId())) {
            //2.创建加签人的任务并执行完毕
            org.flowable.task.api.Task task = this.createSubTask(taskEntity, finalParentTaskId, operator);
            taskId = task.getId();
        }
        Task taskInfo = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (null != taskInfo) {
            taskService.complete(taskId);
        }
        //如果是候选人，需要删除运行时候选表种的数据。
        long candidateCount = taskService.createTaskQuery().taskId(parentTaskId).taskCandidateUser(operator).count();
        if (candidateCount > 0) {
            taskService.deleteCandidateUser(parentTaskId, operator);
        }
    }

    @Override
    public DefinitionSupplementary queryDefinitionSupplementary(String taskDefId) {
        return definitionSupplementaryDao.findByTaskDefId(taskDefId);
    }
}
